/******************************************************************************

 @File         OGLES2HelloTriangle_Qt.cpp

 @Title        OpenGL ES 2.0 Hello Triangle Tutorial

 @Version      

 @Copyright    Copyright (C)  Imagination Technologies Limited.

 @Platform     .

 @Description  Basic Tutorial that shows step-by-step how to initialize OpenGL ES
               2.0, use it for drawing a triangle and terminate it.

******************************************************************************/
#include <stdio.h>
#include "QApplication"
#include "QMainWindow"
#include "QDesktopWidget"
#include "OGLES2HelloTriangle.h"

/******************************************************************************
 Defines
******************************************************************************/

// Index to bind the attributes to vertex shaders
#define VERTEX_ARRAY	0

COGLES2HelloTriangle::COGLES2HelloTriangle(QWidget *parent) : QGLWidget(parent),
                                                              m_ui32Frame(0)
{
     setAttribute(Qt::WA_PaintOnScreen);
     setAttribute(Qt::WA_NoSystemBackground);
}

COGLES2HelloTriangle::~COGLES2HelloTriangle()
{
}

void COGLES2HelloTriangle::initializeGL()
{
    // Fragment and vertex shaders code
    const char* pszFragShader = "\
        void main (void)\
        {\
            gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\
        }";

    const char* pszVertShader = "\
        attribute highp vec4	myVertex;\
        uniform mediump mat4	myPMVMatrix;\
        void main(void)\
        {\
            gl_Position = myPMVMatrix * myVertex;\
        }";

    // Create the fragment shader object
    m_uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);

    // Load the source code into it
    glShaderSource(m_uiFragShader, 1, (const char**)&pszFragShader, NULL);

    // Compile the source code
    glCompileShader(m_uiFragShader);

    // Check if compilation succeeded
    GLint bShaderCompiled;
    glGetShaderiv(m_uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);

    if(!bShaderCompiled)
    {
        // An error happened, first retrieve the length of the log message
        int i32InfoLogLength, i32CharsWritten;
        glGetShaderiv(m_uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);

        // Allocate enough space for the message and retrieve it
        char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);

        // Displays the error
        printf("Failed to compile fragment shader: %s\n", pszInfoLog);
        delete [] pszInfoLog;
        exit(0);
    }

    // Loads the vertex shader in the same way
    m_uiVertShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(m_uiVertShader, 1, (const char**)&pszVertShader, NULL);
    glCompileShader(m_uiVertShader);
    glGetShaderiv(m_uiVertShader, GL_COMPILE_STATUS, &bShaderCompiled);

    if(!bShaderCompiled)
    {
        int i32InfoLogLength, i32CharsWritten;
        glGetShaderiv(m_uiVertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
        char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiVertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
        printf("Failed to compile vertex shader: %s\n", pszInfoLog);
        delete [] pszInfoLog;
        exit(0);
    }

    // Create the shader program
    m_uiProgramObject = glCreateProgram();

    // Attach the fragment and vertex shaders to it
    glAttachShader(m_uiProgramObject, m_uiFragShader);
    glAttachShader(m_uiProgramObject, m_uiVertShader);

    // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
    glBindAttribLocation(m_uiProgramObject, VERTEX_ARRAY, "myVertex");

    // Link the program
    glLinkProgram(m_uiProgramObject);

    // Check if linking succeeded in the same way we checked for compilation success
    GLint bLinked;
    glGetProgramiv(m_uiProgramObject, GL_LINK_STATUS, &bLinked);

    if(!bLinked)
    {
        int ui32InfoLogLength, ui32CharsWritten;
        glGetProgramiv(m_uiProgramObject, GL_INFO_LOG_LENGTH, &ui32InfoLogLength);
        char* pszInfoLog = new char[ui32InfoLogLength];
        glGetProgramInfoLog(m_uiProgramObject, ui32InfoLogLength, &ui32CharsWritten, pszInfoLog);
        printf("Failed to link program: %s\n", pszInfoLog);
        delete [] pszInfoLog;
        exit(0);
    }

    // Actually use the created program
    glUseProgram(m_uiProgramObject);

    // Sets the clear color.
    // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
    glClearColor(0.6f, 0.8f, 1.0f, 1.0f); // clear blue

    // We're going to draw a triangle to the screen so create a vertex buffer object for our triangle

    // Interleaved vertex data
    GLfloat afVertices[] = {	-0.4f,-0.4f,0.0f, // Position
                                 0.4f ,-0.4f,0.0f,
                                 0.0f ,0.4f ,0.0f};

    // Generate the vertex buffer object (VBO)
    glGenBuffers(1, &m_ui32Vbo);

    // Bind the VBO so we can fill it with data
    glBindBuffer(GL_ARRAY_BUFFER, m_ui32Vbo);

    // Set the buffer's data
    unsigned int uiSize = 3 * (sizeof(GLfloat) * 3); // Calc afVertices size (3 vertices * stride (3 GLfloats per vertex))
    glBufferData(GL_ARRAY_BUFFER, uiSize, afVertices, GL_STATIC_DRAW);
}

void COGLES2HelloTriangle::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

void COGLES2HelloTriangle::paintGL()
{
    // Matrix used for projection model view (PMVMatrix)
    static const float pfIdentity[] =
    {
        1.0f,0.0f,0.0f,0.0f,
        0.0f,1.0f,0.0f,0.0f,
        0.0f,0.0f,1.0f,0.0f,
        0.0f,0.0f,0.0f,1.0f
    };

    /*
        Clears the color buffer.
        glClear() can also be used to clear the depth or stencil buffer
        (GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT)
    */
    glClear(GL_COLOR_BUFFER_BIT);

    /*
        Bind the projection model view matrix (PMVMatrix) to
        the associated uniform variable in the shader
    */

    // First gets the location of that variable in the shader using its name
    int i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");

    // Then passes the matrix to that variable
    glUniformMatrix4fv( i32Location, 1, GL_FALSE, pfIdentity);

    /*
        Enable the custom vertex attribute at index VERTEX_ARRAY.
        We previously binded that index to the variable in our shader "vec4 MyVertex;"
     */
    glEnableVertexAttribArray(VERTEX_ARRAY);

    // Sets the vertex data to this attribute index
    glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0);

    /*
        Draws a non-indexed triangle array from the pointers previously given.
        This function allows the use of other primitive types : triangle strips, lines, ...
        For indexed geometry, use the function glDrawElements() with an index list.
    */
    glDrawArrays(GL_TRIANGLES, 0, 3);

    if(m_ui32Frame > 500)
    {
        // Tidy up before exit

        // Frees the OpenGL handles for the program and the 2 shaders
        glDeleteProgram(m_uiProgramObject);
        glDeleteShader(m_uiFragShader);
        glDeleteShader(m_uiVertShader);

        // Delete the VBO as it is no longer needed
        glDeleteBuffers(1, &m_ui32Vbo);

        exit(0); // We wish to exit our app
    }

    ++m_ui32Frame;

    // update our widget so paintGL is called again.
    update();
}

/*!****************************************************************************
 @Function	main
 @Input		argc		Number of arguments
 @Input		argv		Command line arguments
 @Return	int		result code to OS
 @Description	Main function of the program
******************************************************************************/
int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    QMainWindow window;
    QDesktopWidget *desktop = QApplication::desktop();

    // set our CentralWidget. The window will take care of deleting
    // COGLES2HelloTriangle when the time is right
    window.setCentralWidget(new COGLES2HelloTriangle());

    // Resize to fit our screen dimensions
    window.resize(desktop->width(), desktop->height());

    // Show the window full screen
    window.showFullScreen();

    return app.exec();
}

/******************************************************************************
 End of file (OGLES2HelloTriangle_Qt.cpp)
******************************************************************************/

